using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System;
using Microsoft.Xna.Framework.Media;

namespace WPCoco.Audio
{
    /// <summary>
    /// Class for playing Music and Fx
    /// </summary>
    public class AudioManager : GameComponent
    {
        #region Singleton

        static AudioManager shared = null;

        public static AudioManager Shared
        {
            get 
            {
                if (shared == null)
                    shared = new AudioManager(Coco.Shared.Game);
                return shared; 
            }
        }

        #endregion

        private AudioManager(Game game)
            : base(game) 
        {
            MusicVolumen = 0.5f;
            EffectVolumen = 1.0f;
        }

        Dictionary<string, SoundEffect> soundBank = new Dictionary<string, SoundEffect>();
        Dictionary<string, List<SoundEffectInstance>> soundInstances = new Dictionary<string, List<SoundEffectInstance>>();

        Dictionary<string, Song> musicBank = new Dictionary<string, Song>();

        string currentMusicKey = null;
        /// <summary>
        /// Key of current music
        /// </summary>
        public string CurrentMusicKey
        {
            get { return currentMusicKey; }
            private set { currentMusicKey = value; }
        }

        float musicVolumen;
        /// <summary>
        /// Music volume
        /// </summary>
        public float MusicVolumen
        {
            get { return musicVolumen; }
            set 
            {
                value = (float)Math.Max(Math.Min(value, 1), 0);
                if (musicVolumen == value) return;
                musicVolumen = value;
                MediaPlayer.Volume = musicVolumen;
            }
        }

        float effectVolumen;
        /// <summary>
        /// Sound effect volume
        /// </summary>
        public float EffectVolumen
        {
            get { return effectVolumen; }
            set 
            {
                value = (float)Math.Max(Math.Min(value, 1), 0);
                effectVolumen = value; 
            }
        }

        const int MaxEffectInstances = 5;

        /// <summary>
        /// Load one sound effect 
        /// </summary>
        /// <param name="key">Key to use for that sounf effect</param>
        /// <param name="resourcePath">Source path for the resource</param>
        public void LoadSound(string key, string resourcePath) 
        {
            if (soundBank.ContainsKey(key)) return;
            SoundEffect sound = Coco.Load<SoundEffect>(resourcePath);
            soundBank[key] = sound;
            soundInstances[key] = new List<SoundEffectInstance>();
        }

        /// <summary>
        /// Unload a specified sound effect
        /// </summary>
        /// <param name="key">Key of the sound effect to unload</param>
        public void UnloadSound(string key) 
        {
            if (!soundBank.ContainsKey(key)) return;
            List<SoundEffectInstance> instances = soundInstances[key];
            for (int i = 0; i < instances.Count; i++)
            {
                if (instances[i].State == SoundState.Playing || instances[i].State == SoundState.Paused)
                    instances[i].Stop(true);
                instances[i].Dispose();
            }
            instances.Clear();
            soundBank.Remove(key);
        }

        /// <summary>
        /// Load one music
        /// </summary>
        /// <param name="key">Key to use for that music</param>
        /// <param name="resourcePath">Source path for the resource</param>
        public void LoadMusic(string key, string resourcePath) 
        {
            if (musicBank.ContainsKey(key)) return;
            Song song = Coco.Load<Song>(resourcePath);
            musicBank[key] = song;
        }

        /// <summary>
        /// Unload a specified music
        /// </summary>
        /// <param name="key"></param>
        public void UnloadMusic(string key) 
        {
            if (!musicBank.ContainsKey(key)) return;
            Song song = musicBank[key];
            musicBank.Remove(key);
            song.Dispose();
        }


        /// <summary>
        /// Play an specified sounf effect
        /// </summary>
        /// <param name="soundName">Key of the sounf to play</param>
        public void PlaySound(string soundName)
        {
            if (soundBank.ContainsKey(soundName))
            {
                List<SoundEffectInstance> instances = soundInstances[soundName];
                for (int i = 0; i < instances.Count; i++)
                {
                    if (instances[i].State == SoundState.Stopped)
                    {
                        instances[i].Volume = effectVolumen;
                        instances[i].Play();
                        return;
                    }
                }
                if (instances.Count < MaxEffectInstances)
                {
                    SoundEffectInstance sound = soundBank[soundName].CreateInstance();
                    instances.Add(sound);
                    sound.Volume = effectVolumen;
                    sound.Play();
                }
            }
        }

        /// <summary>
        /// Stop playing one specified sound effect
        /// </summary>
        /// <param name="soundName">Key for the sound effect to stop</param>
        public void StopSound(string soundName)
        {
            if (soundBank.ContainsKey(soundName))
            {
                List<SoundEffectInstance> instances = soundInstances[soundName];
                for (int i = 0; i < instances.Count; i++)
                {
                    if (instances[i].State == SoundState.Playing)
                        instances[i].Stop();
                }
            }
        }

        /// <summary>
        /// Stop all sound effects
        /// </summary>
        public void StopSounds()
        {
            foreach (var key in soundBank.Keys)
                StopSound(key);
        }

        /// <summary>
        /// Pause or resume all sound effect
        /// </summary>
        /// <param name="isPause"></param>
        public void PauseResumeSounds(bool isPause)
        {
            foreach (var key in soundBank.Keys)
            {
                if (isPause)
                {
                    List<SoundEffectInstance> instances = soundInstances[key];
                    for (int i = 0; i < instances.Count; i++)
                    {
                        if (instances[i].State == SoundState.Playing)
                            instances[i].Pause();
                    }
                    MediaPlayer.Pause();
                }
                else
                {
                    List<SoundEffectInstance> instances = soundInstances[key];
                    for (int i = 0; i < instances.Count; i++)
                    {
                        if (instances[i].State == SoundState.Paused)
                            instances[i].Resume();
                    }
                    MediaPlayer.Resume();
                }
            }
        }


        /// <summary>
        /// Play a music, you can have just one music playing a the same time, 
        /// use this for background
        /// </summary>
        /// <param name="musicSoundName">Music key</param>
        /// <param name="isLooped">Set it to true if you whant that is loops</param>
        public void PlayMusic(string musicSoundName, bool isLooped)
        {
            if (!musicBank.ContainsKey(musicSoundName)) return;
            if (currentMusicKey == musicSoundName) return;
            currentMusicKey = musicSoundName;
            Song s = musicBank[musicSoundName];
            if (MediaPlayer.GameHasControl)
            {
                MediaPlayer.Volume = musicVolumen;
                MediaPlayer.IsRepeating = isLooped;
                MediaPlayer.Play(s);
            }
        }

        /// <summary>
        /// Stop playing the music
        /// </summary>
        public void StopMusic()
        {
            MediaPlayer.Stop();
            currentMusicKey = null;
        }

        /// <summary>
        /// Reset the whole AudioManage data
        /// </summary>
        public void Reset() 
        {
            foreach (var key in soundBank.Keys)
            {
                List<SoundEffectInstance> instances = soundInstances[key];
                for (int i = instances.Count - 1; i >= 0; i++)
                {
                    if (instances[i].State == SoundState.Paused || instances[i].State == SoundState.Playing)
                        instances[i].Stop(true);
                    instances[i].Dispose();
                }
                instances.Clear();
            }
            StopMusic();
        }

        #region Instance Disposal Methods

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    Reset();
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        #endregion
    }
}